﻿using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using Pixysoft.Framework.Verifications;
using Pixysoft.Framework.Configurations.Core;
using System.Xml;

namespace Pixysoft.Framework.Configurations.Controller
{
    class XmlNodeController
    {
        private static volatile XmlNodeController instance;

        private static object syncRoot = new Object();

        public static XmlNodeController Instance
        {
            get
            {
                if (instance == null)
                {
                    lock (syncRoot)
                    {
                        instance = new XmlNodeController();
                    }
                }

                return instance;

            }
        }


        /// <summary>
        /// 根据约束搜索xml
        /// </summary>
        /// <param name="filepath"></param>
        /// <param name="constraints"></param>
        /// <returns></returns>
        public IXmlTree ExecuteReader(string filepath, XmlConstraintCollection constraints)
        {
            IXmlTree tree = null;

            if (!File.Exists(filepath))
                throw new Exception("missing file in path " + filepath);

            using (Stream stream = File.Open(filepath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
            {
                XmlTextReader reader = new XmlTextReader(stream, XmlNodeType.Element, null);

                tree = new XmlReaderTreeState().Read(reader, constraints);

                reader.Close();
            }

            return tree;
        }
        /// <summary>
        /// 树直接保存在本地磁盘
        /// </summary>
        /// <param name="filepath"></param>
        /// <param name="tree"></param>
        public void Save(string filepath, IXmlTree tree)
        {
            using (Stream stream = File.Open(filepath, FileMode.Create))
            {
                XmlWriterSettings setting = new XmlWriterSettings();

                setting.Indent = true;

                setting.OmitXmlDeclaration = true;

                XmlWriter writer = XmlWriter.Create(stream, setting);// new XmlTextWriter(stringWriter);

                SerializeAll(writer, tree);

                writer.Flush();

                writer.Close();

                stream.Close();

                stream.Dispose();
            }
        }
        /// <summary>
        /// 序列化树
        /// </summary>
        /// <param name="writer"></param>
        /// <param name="tree"></param>
        private void SerializeAll(XmlWriter writer, IXmlTree tree)
        {
            writer.WriteProcessingInstruction("xml", "version=\"1.0\" encoding=\"utf-8\"");

            foreach (IXmlNode node in tree.Node)
            {
                PrivateSerialize(writer, node);
            }

            writer.Close();
        }


        /// <summary>
        /// 树根据节点序列化为集合
        /// </summary>
        /// <param name="tree"></param>
        /// <param name="indent"></param>
        /// <returns></returns>
        public List<string> Serialize(IXmlTree tree, bool indent)
        {
            List<string> list = new List<string>();

            foreach (IXmlNode node in tree.Node)
            {
                StringWriter stringWriter = new StringWriter();

                XmlWriterSettings setting = new XmlWriterSettings();

                setting.Indent = indent;

                setting.OmitXmlDeclaration = true;

                XmlWriter writer = XmlWriter.Create(stringWriter, setting);// new XmlTextWriter(stringWriter);


                PrivateSerialize(writer, node);

                writer.Flush();

                writer.Close();

                list.Add(stringWriter.ToString());
            }

            return list;

        }
        /// <summary>
        /// 序列化树
        /// </summary>
        /// <param name="tree"></param>
        /// <param name="indent"></param>
        /// <returns></returns>
        public string SerializeAll(IXmlTree tree, bool indent)
        {
            StringBuilder builder = new StringBuilder();

            foreach (IXmlNode node in tree.Node)
            {
                StringWriter stringWriter = new StringWriter();

                XmlWriterSettings setting = new XmlWriterSettings();

                setting.Indent = indent;

                setting.OmitXmlDeclaration = true;

                XmlWriter writer = XmlWriter.Create(stringWriter, setting);// new XmlTextWriter(stringWriter);

                PrivateSerialize(writer, node);

                writer.Flush();

                writer.Close();

                builder.Append(stringWriter.ToString());

                if (indent)
                    builder.AppendLine();
            }

            return builder.ToString();

        }


        /// <summary>
        /// 节点序列化为流
        /// </summary>
        /// <param name="node"></param>
        /// <returns></returns>
        public string Serialize(IXmlNode node)
        {
            return Serialize(node, false);
        }
        /// <summary>
        /// 节点序列化为流 是否缩进
        /// </summary>
        /// <param name="node"></param>
        /// <param name="indent"></param>
        /// <returns></returns>
        public string Serialize(IXmlNode node, bool indent)
        {
            StringWriter stringWriter = new StringWriter();

            XmlWriterSettings setting = new XmlWriterSettings();

            setting.Indent = indent;

            setting.OmitXmlDeclaration = true;

            XmlWriter writer = XmlWriter.Create(stringWriter, setting);// new XmlTextWriter(stringWriter);

            //XmlWriter writer = new XmlTextWriter(stringWriter);

            PrivateSerialize(writer, node);

            writer.Flush();

            writer.Close();

            return stringWriter.ToString();

        }
        /// <summary>
        /// 节点序列化为流
        /// </summary>
        /// <param name="writer"></param>
        /// <param name="node"></param>
        private void PrivateSerialize(XmlWriter writer, IXmlNode node)
        {
            string[] elementnames = node.ElementName.Split(new char[] { ':' }, StringSplitOptions.RemoveEmptyEntries);

            if (elementnames.Length > 1)
            {
                writer.WriteStartElement(elementnames[0], elementnames[1], elementnames[0]); // prefix = ns
            }
            else
            {
                writer.WriteStartElement(node.ElementName);
            }

            foreach (IXmlProperty property in node.Properties)
            {
                if (property.Key.StartsWith(StringHelper.default_xmlattribute_xmlns, StringComparison.OrdinalIgnoreCase))
                    continue;

                writer.WriteAttributeString(property.Key, property.Value);
            }

            foreach (IXmlNode subnode in node.Node)
            {
                PrivateSerialize(writer, subnode);
            }

            writer.WriteString(node.Text);

            writer.WriteEndElement();
        }


        /// <summary>
        /// 反序列化为节点
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public IXmlNode DeserializeToNode(string value)
        {
            IXmlNode node = null;


            using (XmlTextReader reader = new XmlTextReader(value, XmlNodeType.Element, null))
            {
                //20100128 设置为false 就不会检查前缀

                reader.Namespaces = false;

                reader.Read();
                try
                {
                    node = new XmlReaderNodeState().Read(reader);
                }
                catch (Exception ex)
                {
                    throw new Exception("deserialize node fail. value = " + value, ex);
                }
                finally
                {
                    reader.Close();
                }
            }


            return node;
        }
        /// <summary>
        /// 反序列化为节点
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public IXmlNode DeserializeToNode(byte[] value)
        {
            IXmlNode node = null;

            using (Stream stream = new MemoryStream(value))
            {
                XmlTextReader reader = new XmlTextReader(stream, XmlNodeType.Element, null);

                //20100128 设置为false 就不会检查前缀

                reader.Namespaces = false;

                reader.Read();

                try
                {
                    node = new XmlReaderNodeState().Read(reader);
                }
                catch (Exception ex)
                {
                    throw new Exception("deserialize node fail. value = " + value, ex);
                }
                finally
                {
                    reader.Close();
                }
            }

            return node;
        }
        /// <summary>
        /// 反序列化为树
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public IXmlTree DeserializeToTree(byte[] value)
        {
            IXmlTree tree = null;

            using (Stream stream = new MemoryStream(value))
            {
                XmlTextReader reader = new XmlTextReader(stream, XmlNodeType.Element, null);

                tree = new XmlReaderTreeState().Read(reader);

                reader.Close();
            }

            return tree;
        }


        /// <summary>
        /// 从指定节点开始寻找
        /// </summary>
        class XmlReaderTreeState
        {
            public IXmlTree Read(XmlReader reader)
            {
                //2010-11-18 reset to first element.

                //2010-12-06 不能够开始就循环 会导致忽略了第一个element

                //while (reader.NodeType != XmlNodeType.Element)
                //{
                //    reader.Read();
                //}

                IXmlTree tree = new XmlTree();

                while (reader.Read())
                {
                    switch (reader.NodeType)
                    {
                        case XmlNodeType.Element:
                            {
                                tree.Nodes.Add(new XmlReaderNodeState().Read(reader));

                                break;
                            }
                        default:
                            {
                                break;
                            }
                    }
                }

                return tree;
            }

            public IXmlTree Read(XmlReader reader, XmlConstraintCollection constraints)
            {
                //2010-11-18 reset to first element.

                //2010-12-06 不能够开始就循环 会导致忽略了第一个element

                //while (reader.NodeType != XmlNodeType.Element)
                //{
                //    reader.Read();
                //}

                IXmlTree tree = new XmlTree();

                while (reader.Read())
                {
                    switch (reader.NodeType)
                    {
                        case XmlNodeType.Element:
                            {
                                string elementname = reader.Name;

                                if (!string.Equals(elementname, constraints.ElementName, StringComparison.OrdinalIgnoreCase))
                                    break;

                                //如果没有约束 则默认不操作

                                //09-12-09 如果没有约束，则直接读取内部信息

                                if (constraints.HasProperty)
                                {
                                    if (!reader.HasAttributes)
                                        break;

                                    bool meets = true;

                                    //09-11-28 由于attributeName 区分大小写 必须循环reader

                                    for (int i = 0; i < reader.AttributeCount; i++)
                                    {
                                        reader.MoveToAttribute(i);

                                        string attributename = reader.Name;

                                        if (string.IsNullOrEmpty(attributename))
                                            continue;

                                        attributename = attributename.Trim();

                                        string attributevalue = reader.Value;

                                        if (string.IsNullOrEmpty(attributevalue))
                                            attributevalue = "";
                                        else
                                            attributevalue.Trim();

                                        if (!Meets(attributename, attributevalue, constraints))
                                        {
                                            meets = false;

                                            break;
                                        }
                                    }

                                    if (!meets)
                                        break;

                                    reader.MoveToElement();
                                }

                                tree.Nodes.Add(new XmlReaderNodeState().Read(reader));

                                break;
                            }
                        default:
                            {
                                break;
                            }
                    }
                }

                return tree;
            }

            private bool Meets(string attributeName, string attributeValue, XmlConstraintCollection constraints)
            {
                //2010-08-31 有可能就是空值

                //if (string.IsNullOrEmpty(attributeValue))
                //    return false;

                //先判断 or 只要有一个是true 都是true

                foreach (IXmlConstraintProperty property in constraints.Ors)
                {
                    if (!string.Equals(property.PropertyName, attributeName, StringComparison.OrdinalIgnoreCase))
                        continue;

                    if (property.Meets(attributeValue))
                        return true;
                }

                //再判断 and 只要有一个false 都是false

                foreach (IXmlConstraintProperty property in constraints.Ands)
                {
                    if (!string.Equals(property.PropertyName, attributeName, StringComparison.OrdinalIgnoreCase))
                        continue;

                    if (!property.Meets(attributeValue))
                        return false;
                }

                return true;
            }
        }
        /// <summary>
        /// 主要负责 节点的属性初始化、同时递归节点的子节点初始化
        /// </summary>
        class XmlReaderNodeState
        {
            internal IXmlNode Read(XmlReader reader)
            {
                //2010-11-18 reset to first element.

                while (reader.NodeType != XmlNodeType.Element)
                {
                    if (!reader.Read())
                    {
                        throw Exceptions.UnexpectedResultException("reader.read fail.");
                    }
                }

                //初始化节点名称

                string nodename = reader.Name;

                if (string.IsNullOrEmpty(nodename))
                    throw new Exception("missing nodename in node, please check config.");

                //09-11-25 区分大小写

                nodename = nodename.Trim();

                IXmlNode node = new Pixysoft.Framework.Configurations.Core.XmlNode(nodename);

                if (reader.HasAttributes)
                {
                    for (int i = 0; i < reader.AttributeCount; i++)
                    {
                        reader.MoveToAttribute(i);

                        string attributename = reader.Name;

                        if (string.IsNullOrEmpty(attributename))
                            continue;

                        attributename = attributename.Trim();

                        string attributevalue = reader.Value;

                        if (string.IsNullOrEmpty(attributevalue))
                            attributevalue = "";
                        else
                            attributevalue.Trim();

                        node.Properties.Add(attributename, attributevalue);
                    }
                }

                //09-11-24 如果没有标签 返回false 但是不影响操作

                reader.MoveToElement();

                if (reader.IsEmptyElement)
                    return node;

                while (reader.Read())
                {
                    switch (reader.NodeType)
                    {
                        case XmlNodeType.CDATA://2010-11-11 支持cdata格式
                        case XmlNodeType.Text:
                            {
                                string value = reader.Value;

                                if (string.IsNullOrEmpty(value))
                                    value = "";

                                //不能用toupper

                                node.Text = value.Trim();

                                break;
                            }
                        case XmlNodeType.Element:
                            {
                                node.Nodes.Add(new XmlReaderNodeState().Read(reader));

                                //09-11-28 不能在这里判断 如果是递归 则内部已经处理了

                                //if (reader.IsEmptyElement)
                                //    return node;

                                break;
                            }
                        case XmlNodeType.EndElement:
                            {
                                if (string.Equals(nodename, reader.Name, StringComparison.OrdinalIgnoreCase))
                                    return node;

                                break;
                            }
                        default:
                            {
                                break;
                            }

                    }
                }

                throw new Exception("missing end element tag for node " + nodename);
            }
        }
    }
}
